package com.xtpt.disaster.utils;

import com.alibaba.fastjson.JSON;
import com.xtpt.disaster.bean.*;
import com.xtpt.disaster.common.ClientCmdBuilder;
import com.xtpt.disaster.common.PortalResult;
import com.xtpt.disaster.common.consts.BackupTypeEnum;
import com.xtpt.disaster.common.consts.DataTypeEnum;
import com.xtpt.disaster.common.consts.StatusConst;
import com.xtpt.disaster.service.DBMysqlJobDetailService;
import com.xtpt.disaster.service.DBNodeInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Component
public class BackupRestoreUtil<T extends ClientCmdBuilder> {

    @Autowired
    DBNodeInfoService dbNodeInfoService;

    @Autowired
    DBMysqlJobDetailService dbMysqlJobDetailService;

    @Autowired
    ServerPathUtil serverPathUtil;

    public PortalResult backup(ParametersDBBackup backupInfo,T cmdBuilder)
    {
        DbnodeInfo dbNodeInfo=dbNodeInfoService.selectByPrimaryKey(backupInfo.getDbId());
        JobDetail jobDetail = JobDetailUtil.generateBackUpJobDetail(backupInfo,dbNodeInfo.getNodeDbType(),backupInfo.getDbId());
        try {
            // 备份拼接文件名
            String startTimeStr = TimeUtil.dateToString(jobDetail.getJobStartTime());
            String backupName = dbNodeInfo.getNodeDbName() + "_" + startTimeStr;
            // 设置客户端信息用于命令生成
            cmdBuilder.initializeWithClientNode(dbNodeInfo);
            // 生成回调地址
            String resultReceiver = serverPathUtil.generateCallbackUrl("/rest/task/receiver/"+jobDetail.getJobId());
            // 设置回调地址
            cmdBuilder.setResultCallBackUrl(resultReceiver);
            BackupTypeEnum backupType = BackupTypeEnum.getObject(backupInfo.getTaskBackupType());
            // 生成备份所需命令组并返回以 备份文件在服务器的路径作为key，以备份文件在客户机的路径作为value的map
            Map<String,String> serverToClientFiles=
                    cmdBuilder.dbBackUp(backupInfo.getTaskId().toString(),
                            backupName,
                            backupType.toString(),
                            backupInfo.getTaskDesc()+"("+jobDetail.getJobId()+")",
                            jobDetail.getJobId().toString()
                    );
            // 将备份命令转化为json字符串
            String cmd=cmdBuilder.buildToJSONString();
            // 保存命令
            jobDetail.setJobCommand(cmd);
            // 设置数据类型
            jobDetail.setDataType(backupInfo.getTaskFileType());
            StringBuilder serverFilesPath= new StringBuilder();
            StringBuilder clientFilesPath= new StringBuilder();
            for (Map.Entry<String,String> entry:serverToClientFiles.entrySet()){
                serverFilesPath.append(entry.getKey()).append(",");
                clientFilesPath.append(entry.getValue()).append(",");
            }
            // 保存服务端路径
            jobDetail.setServerPath(serverFilesPath.deleteCharAt(serverFilesPath.length()-1).toString());
            // 保存客户端路径
            jobDetail.setClientPath(clientFilesPath.deleteCharAt(clientFilesPath.length()-1).toString());
            // 将job记录存入表中
            int count = dbMysqlJobDetailService.insert(jobDetail);
            if(count==0) {
                return PortalResult.error("备份记录到作业表中时失败");
            }
            System.out.println("提交到客户端！");
            // 将命令提交到代理端执行
            String postToClient = cn.hutool.http.HttpUtil.post(dbNodeInfo.getNodeClientAgentUrl(),cmd);
            System.out.println("提交到客户端结束！");
            // 获取代理对命令的响应
            PortalResult portalResult = JSON.parseObject(postToClient,PortalResult.class);
            if(!portalResult.getStatus().equals(StatusConst.SUCCESS))
            {
                jobDetail.setJobStatus(StatusConst.ERROR);
                jobDetail.setJobResult("请求代理客户端失败："+portalResult);
            }
        } catch (Exception e) {
            jobDetail.setJobStatus(StatusConst.ERROR);
            jobDetail.setJobResult(e.getMessage());
            e.printStackTrace();
        }
        if(jobDetail.getJobStatus().equals(StatusConst.ERROR))
        {
            return updateErrorStatus(jobDetail);
        }
        return PortalResult.ok("备份任务执行中，请稍后查看执行结果！");
    }
    /**
     * @Description: 专用于更新失败情况下的状态
     * @Author: hpc
     * @Date: 2021/12/27 下午 3:58
     * @param jobDetail:
     * @return: com.xtpt.disaster.common.PortalResult
     **/
    public PortalResult updateErrorStatus(JobDetail jobDetail)
    {
        // 将job记录存入表中
        int count = dbMysqlJobDetailService.insertOrUpdate(jobDetail);
        if(count>0) {
            return PortalResult.error("备份失败记录已更新到作业表中！");
        } else {
            return PortalResult.error("备份记录更新到作业表中时失败！");
        }
    }
    /**
     * @Description: 数据库恢复公共类
     * @Author: hpc
     * @Date: 2021/12/27 下午 3:46
     * @param recoverInfo:
     * @param cmdBuilder:
     * @return: com.xtpt.disaster.common.PortalResult
     **/
    public PortalResult recover(ParametersDBRecover recoverInfo,T cmdBuilder)
    {
        String errorMsg="";
        JobDetail backupJobDetail = dbMysqlJobDetailService.selectByPrimaryKey(recoverInfo.getJobDetailId());
        if(backupJobDetail==null)
        {
            return PortalResult.error(String.format("jobId: %s 不存在！", recoverInfo.getJobDetailId().toString()));
        }
        Long dbID=backupJobDetail.getDbId();
        if(dbID==null)
        {
            return PortalResult.error("dbID不能为null！");
        }
        DbnodeInfo client=dbNodeInfoService.selectByPrimaryKey(dbID);
        JobDetail jobDetail = JobDetailUtil.generateRestoreJobDetail(
                backupJobDetail.getTaskId(),
                recoverInfo.getTaskName(),
                client.getNodeDbType(),
                dbID,
                recoverInfo.getTaskDesc());
        try {
            // 设置客户端信息用于命令生成
            cmdBuilder.initializeWithClientNode(client);
            // 生成回调地址
            String resultReceiver = serverPathUtil.generateCallbackUrl("/rest/task/receiver/"+jobDetail.getJobId());
            // 设置回调地址
            cmdBuilder.setResultCallBackUrl(resultReceiver);
            List<String> allUsedBackupFiles=new ArrayList<>();
            allUsedBackupFiles.add(backupJobDetail.getServerPath());
            cmdBuilder.dbRestore(backupJobDetail.getClientPath(),allUsedBackupFiles);
            // 将备份命令转化为json字符串
            String cmd=cmdBuilder.buildToJSONString();
            // 保存job发出的cmd记录
            jobDetail.setJobCommand(cmd);
            jobDetail.setDataType(DataTypeEnum.DATABASE.getValue());
            // 将job记录存入表中
            int count = dbMysqlJobDetailService.insert(jobDetail);
            if(count==0) {
                return PortalResult.error("恢复记录插入到作业表中失败！");
            }
            // 将命令提交到代理端执行
            String postToClient = cn.hutool.http.HttpUtil.post(client.getNodeClientAgentUrl(),cmd);
            // 获取代理对命令的响应
            PortalResult portalResult = JSON.parseObject(postToClient,PortalResult.class);
            if(!portalResult.getStatus().equals(StatusConst.SUCCESS))
            {
                jobDetail.setJobResult("请求代理客户端失败:"+postToClient);
                jobDetail.setJobStatus(StatusConst.ERROR);
            }
        } catch (Exception e) {
            jobDetail.setJobResult(e.getMessage());
            jobDetail.setJobStatus(StatusConst.ERROR);
            e.printStackTrace();
        }
        // 将job记录存入表中
        if(jobDetail.getJobStatus().equals(StatusConst.ERROR))
        {
            return updateErrorStatus(jobDetail);
        }
        return PortalResult.ok("恢复任务执行中，请稍后查看执行结果！");
    }

}
